﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lucene.Net.Analysis.Standard;
using Common;
using Lucene.Net.Index;
using Lucene.Net.Analysis;
using Lucene.Net.Store;
using Lucene.Net.Search;
using Lucene.Net.Highlight;

namespace LuceneManager
{
    public class LuceneSettings
    {
        public IndexNode[] IndexNodes { get; set; }
        public string PrefixFilePath { get; set; }
        public LuceneSettings(IndexNode[] indexNodes, string prefixFilePath)
        {
            IndexNodes = indexNodes;
            PrefixFilePath = prefixFilePath;
        }
    }
    public class LuceneManager : IDisposable
    {
        LuceneReader _reader; //this reader serves this indexNode
        string _indexPath;
        IndexWriter _indexWriter;
        bool _createIndex;
        Analyzer _analyzer;
        string _prefixFilePath;
        Directory _indexToDir; // used for in-memory indexing.
        Directory _indexLookupDir; //used for index lookup (check document exist etc)
        object addLock = new object();
        /// <summary>
        /// This instance of LuceneManager Indexes to Disk.
        /// </summary>
        
        /// <param name="createIndex">create a new index (overwrite existing one in directory)</param>
        /// <param name="ramOnly">use RAM index if creating a new index</param>
        /// <param name="prefixFilePath"></param>
        /// /// <param name="indexToPath">Path on the disk to index to</param>
        /// <param name="indexLookupPath">Path to look up stuff in</param>
        public LuceneManager(bool createIndex, bool ramOnly, string prefixFilePath, string indexToPath, string indexLookupPath)
        {
            _indexPath = indexToPath;
            _createIndex = createIndex;
            _analyzer = new HebrewAnalyzer(prefixFilePath);
            _prefixFilePath = prefixFilePath;
          
            if (ramOnly)
                _indexToDir = new RAMDirectory(indexToPath);
            else
            {
                _indexToDir = Lucene.Net.Store.FSDirectory.GetDirectory(indexToPath);
                //_indexToDir.SetLockFactory(new NativeFSLockFactory(indexToPath + @"\locks"));
            }
            _indexLookupDir = Lucene.Net.Store.FSDirectory.GetDirectory(indexLookupPath);
            CreateIndexIfNotExist(_indexToDir);
            _indexWriter = GetIndexWriter(_indexToDir, _createIndex, _analyzer);
            _reader = new LuceneReader(new IndexNode[] { new IndexNode(indexToPath) });

        }

        void CreateIndexIfNotExist(Directory dir)
        {
            if (!IndexReader.IndexExists(dir))
            {
                IndexWriter w = new IndexWriter(dir, new SimpleAnalyzer(), true);
                w.Close();
                
            }
        }

        /// <summary>
        /// This instance of LuceneManager Indexes to memory.
        /// </summary>
        /// <param name="createIndex"></param>
        /// <param name="prefixFilePath"></param>
        public LuceneManager(bool createIndex, string prefixFilePath)
        {
            _createIndex = createIndex;
            _analyzer = new HebrewAnalyzer(prefixFilePath);
            _prefixFilePath = prefixFilePath;
            _indexToDir = new RAMDirectory();
            _indexLookupDir = _indexToDir;
            CreateIndexIfNotExist(_indexLookupDir);
        }

        public IndexWriter GetIndexWriter(Directory dir, bool createIndex, Analyzer analyzer)
        {

            IndexWriter Iw = new IndexWriter(dir, analyzer, createIndex);
            int mergeFactor = 100;
            int minMergeDocs = 100;
            int maxMergeDocs = 5000;
            Iw.SetMergeFactor(mergeFactor);
            Iw.SetMaxMergeDocs(maxMergeDocs);
            Iw.SetMaxBufferedDocs(minMergeDocs);
            Iw.SetRAMBufferSizeMB(100);
            return Iw;
        }

     

        public static Document LuceneDocumentToKnowfirstDocument(Lucene.Net.Documents.Document doc)
        {
            Document kdoc = new Document(Int32.Parse(doc.GetField("ID").StringValue()),
                DateTime.Parse(doc.GetField("DateTime").StringValue()),
                new Uri(doc.GetField("Uri").StringValue()),
                long.Parse(doc.GetField("CommentNumber").StringValue()),
                Int32.Parse(doc.GetField("TalkbackNumber").StringValue()),
                (Document.Type)Enum.Parse(typeof(Document.Type),
                doc.GetField("DocumentType").StringValue()), 0,
                doc.GetField("SiteName").StringValue());
            if (doc.GetField("SentimentID") != null)
                kdoc.SentimentId = Int32.Parse(doc.GetField("SentimentID").StringValue());
            else
                kdoc.SentimentId = 0;
            if (doc.GetField("DownloadDateTime") != null)
                kdoc.DownloadDateTime = DateTime.Parse(doc.GetField("DownloadDateTime").StringValue());
            else
                kdoc.DownloadDateTime = DateTime.MinValue;
            kdoc.Fields[(int)Document.Section.Author] = new Field("", Document.Section.Author);
            kdoc.Fields[(int)Document.Section.Title] = new Field(doc.GetField("Title").StringValue(), Document.Section.Title);
            kdoc.Fields[(int)Document.Section.Summary] = new Field(doc.GetField("Summary").StringValue(), Document.Section.Summary);
            kdoc.Fields[(int)Document.Section.Text] = new Field(doc.GetField("Text").StringValue(), Document.Section.Text);
            return kdoc;
        }

        public static Lucene.Net.Documents.Document CreateLuceneDocument(Document document)
        {
            Lucene.Net.Documents.Document doc = new Lucene.Net.Documents.Document();
            doc.Add(new Lucene.Net.Documents.Field("DateTime", document.DateTime.ToString(), Lucene.Net.Documents.Field.Store.YES,
                Lucene.Net.Documents.Field.Index.UN_TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("Uri", document.Url.ToString(), Lucene.Net.Documents.Field.Store.YES,
              Lucene.Net.Documents.Field.Index.UN_TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("DocumentType", Enum.GetName(typeof(Document.Type), document.DocType), Lucene.Net.Documents.Field.Store.YES,
              Lucene.Net.Documents.Field.Index.UN_TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("Title", document.Fields[0].Text, Lucene.Net.Documents.Field.Store.YES,
              Lucene.Net.Documents.Field.Index.TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("Text", document.Fields[2].Text, Lucene.Net.Documents.Field.Store.YES,
                Lucene.Net.Documents.Field.Index.TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("Summary", document.Fields[1].Text, Lucene.Net.Documents.Field.Store.YES,
                Lucene.Net.Documents.Field.Index.TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("Author", string.Empty, Lucene.Net.Documents.Field.Store.YES,
                  Lucene.Net.Documents.Field.Index.TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("CommentNumber", document.CommentNumber.ToString(), Lucene.Net.Documents.Field.Store.YES,
                   Lucene.Net.Documents.Field.Index.UN_TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("TalkbackNumber", document.talkbackNumber.ToString(), Lucene.Net.Documents.Field.Store.YES,
                   Lucene.Net.Documents.Field.Index.UN_TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("ID", document.ID.ToString(), Lucene.Net.Documents.Field.Store.YES,
                  Lucene.Net.Documents.Field.Index.UN_TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("SiteName", document.SiteName.ToString(), Lucene.Net.Documents.Field.Store.YES,
                  Lucene.Net.Documents.Field.Index.UN_TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("SentimentID", document.SentimentId.ToString(), Lucene.Net.Documents.Field.Store.YES,
                  Lucene.Net.Documents.Field.Index.UN_TOKENIZED));
            doc.Add(new Lucene.Net.Documents.Field("DownloadDateTime", DateTime.Now.ToString(), Lucene.Net.Documents.Field.Store.YES,
                  Lucene.Net.Documents.Field.Index.UN_TOKENIZED));
            return doc;
        }

        public void AddDocument(Document doc)
        {

            if (null == _indexWriter)
            {
                CreateIndexIfNotExist(_indexToDir);
                _indexWriter = GetIndexWriter(_indexToDir, _createIndex, _analyzer);
                //_indexWriter.SetWriteLockTimeout(5000);
            }
            lock (addLock)
            {
                try
                {
                    //if (_reader.GetDocumentByUrlCommentNumber(doc.url.ToString(), doc.CommentNumber) == null)
                    _indexWriter.AddDocument(CreateLuceneDocument(doc));
                }
                catch (Exception e)
                {
                    Console.WriteLine("LuceneManager AddDocument :  " + e.Message);
                }
                //else
                   // _indexWriter.UpdateDocument(new Term("ID", doc.ID.ToString()), CreateLuceneDocument(doc));
            }
        }

        public Document GetDocumentByUrlCommentNumber(string url, long commentNumber)
        {
            _reader = new LuceneReader(new IndexNode[] { new IndexNode(_indexPath) });
            return _reader.GetDocumentByUrlCommentNumber(url, commentNumber);
        }

        public bool WasDocumentIndexed(string Url, long commentNum)
        {
            return (_reader.GetDocumentByUrlCommentNumber(Url, commentNum) != null);
        }

        public void MergeIndexes(string indexPath)
        {
            if (!IndexReader.IndexExists(indexPath))
                return;
            if (null == _indexWriter)
            {
                CreateIndexIfNotExist(_indexToDir);
                _indexWriter = GetIndexWriter(_indexToDir, false, _analyzer);
            }
           
            _indexWriter.AddIndexes(new Directory[] {FSDirectory.GetDirectory(indexPath)});
        }


        public void CloseIndexWriter()
        {
            if (null == _indexWriter)
                return;
            _indexWriter.Optimize();
            _indexWriter.Close();
            _indexWriter = null;
            _indexToDir.Close();
            
           //_indexToDir = null;
        }

        public void OptimizeIndex()
        {

            if (_indexWriter != null)
         
            _indexWriter.Optimize();
        }

        public void FlushIndexWriter()
        {
            if (_indexWriter != null)

                _indexWriter.Flush();
        }

        public void CloseAllFiles()
        {
            if (_indexWriter != null)
                _indexWriter.Close();
            _indexLookupDir.Close();
            _indexToDir.Close();
        }

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            CloseAllFiles();
           
        }

        #endregion

        
    }
}
